
package coop.bancocredicoop.proyectos.gd.alfresco

import java.net.URLEncoder

import org.apache.commons.logging.LogFactory

import org.apache.http.client.utils.URIBuilder
import org.apache.http.util.EntityUtils
import org.apache.http.impl.client.HttpClients
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
import org.apache.http.NameValuePair
import org.apache.http.message.BasicNameValuePair
import org.apache.http.client.entity.UrlEncodedFormEntity
import org.apache.http.entity.mime.MultipartEntityBuilder
import org.apache.http.entity.mime.HttpMultipartMode
import org.apache.http.entity.ContentType

import groovy.json.JsonSlurper

class AlfrescoHttpClient {

    private static final log = LogFactory.getLog(this)

    def alfrescoConfig
    def serviceURL
    def baseURL

    AlfrescoHttpClient(alfrescoConfig) {
        this.alfrescoConfig = alfrescoConfig
        baseURL = alfrescoConfig.baseURL
        serviceURL = alfrescoConfig.serviceURL
    }

    def getUrl(relativeURL) {
        def url = "${baseURL}${relativeURL}"
        new URIBuilder(url)        
    }

    def beforeGet() {}
    def beforePost() {}

    def get(httpClient, relativeURL, parameters=[:]) {
        
        def alfrescoURL = getUrl(relativeURL)

        parameters.each { paramName, paramValue -> 
            alfrescoURL.setParameter(paramName, paramValue)
        }
        def httpGet = new HttpGet(alfrescoURL.build())
        beforeGet(httpClient, httpGet)
        // TODO: Handle errors
        return httpClient.execute(httpGet)
    
    }

    def post(httpClient, relativeURL, parameters=[:]) {        

        def alfrescoURL = getUrl(relativeURL)
        def httpPost = new HttpPost(alfrescoURL.build())

        def postParameters = new ArrayList<NameValuePair>()

        parameters.each { paramName, paramValue -> 
            postParameters.add(new BasicNameValuePair(paramName, paramValue))
        }
        httpPost.setEntity(new UrlEncodedFormEntity(postParameters))
        beforePost(httpPost)
        return httpClient.execute(httpPost)
        
    }

    def postMultipart(httpClient, relativeURL, document, stringParameters=[:]) {

        def alfrescoURL = getUrl(relativeURL)
        def httpPost = new HttpPost(alfrescoURL.build())
        def reqEntityBuilder = MultipartEntityBuilder.create()
        reqEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)

        reqEntityBuilder.addBinaryBody("file", 
                                       document.inputStream, 
                                       ContentType.DEFAULT_BINARY, 
                                       document.originalFilename)
        stringParameters.each { paramName, paramValue -> 
            reqEntityBuilder.addTextBody(paramName, paramValue)
        }

        httpPost.setEntity(reqEntityBuilder.build())
        beforePost(httpPost)
        return httpClient.execute(httpPost)
    }

    def invokeWebScript(executeMethod, wsURL, parameters=[:]) {
        def httpClient = getHttpClient()
    	def response = executeMethod(httpClient, 
                                     getServiceUrl(wsURL), 
                                     parameters)
        def message = parseWebScriptResponse(response)
        response.close()        
        httpClient.close()
        message
    }


    def getServiceUrl(wsURL) {
        "${serviceURL}${wsURL}"
    }

    private def getVersionDownloadUrl(document, encodedDocumentPath) {
        def encodedDocumentName = URLEncoder.encode(document.name)
        
        if (document.lastVersion) {
            def contentServiceURL = getServiceUrl("/cmis/p/${encodedDocumentPath}/${encodedDocumentName}/content.pdf")
            return getUrl(contentServiceURL).toString()
        } 

        def cmisWsURL = "/cmis/p/${encodedDocumentPath}/${encodedDocumentName}/versions"
        def httpClient = getHttpClient()
    	def response = get(httpClient, getServiceUrl(cmisWsURL), [:])
        def httpEntity = response.getEntity()
        def cmisXMLDocument = EntityUtils.toString(httpEntity)
        response.close()        
        
        def feed = new XmlSlurper().parseText(cmisXMLDocument).declareNamespace(
            cmisra: "http://docs.oasis-open.org/ns/cmis/restatom/200908/",
            cmis: "http://docs.oasis-open.org/ns/cmis/core/200908/",
            alf: "http://www.alfresco.org",
            opensearch: "http://a9.com/-/spec/opensearch/1.1/")
        
        def documentVersion = document.documentVersion
        def downloadURL = null
        feed.entry.each { entry ->
            entry.'cmisra:object'.'cmis:properties'.'cmis:propertyString'.each { property ->
                if (property.@propertyDefinitionId.text() == "cmis:versionLabel" && 
                    property.'cmis:value'.text() == documentVersion) {
                        downloadURL = entry.content.@src.text()
                    }
            } 
        }
        downloadURL

    }

    def getContent(document, outputStream) {
        def documentPath = absolutePathForCustomer(document.customer.idPersona)
        def pathParts = documentPath.split("/").findAll { it.trim() != "" } 
        
        def encodedDocumentPath = pathParts.collect { 
                                        URLEncoder.encode(it) 
                                  }[1..pathParts.size()-1].join("/")  
       
        def downloadURL = getVersionDownloadUrl(document, encodedDocumentPath)         

        def httpGet = new HttpGet(downloadURL)
        beforeGet(httpClient, httpGet)
        def downloadResponse = httpClient.execute(httpGet)
        def httpContentEntity = downloadResponse.getEntity()
        outputStream << httpContentEntity.getContent()

        // downloadResponse.close()
        httpClient.close()

    }

	def getContentAsByteArray(document) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream()
		getContent(document, baos)
		baos.toByteArray()
	}
	
    def createFolders(folderPath) {        
        def wsURL = '/coop/bancocredicoop/proyectos/gd/createfolders.json'
        def parameters = [rootPath: this.alfrescoConfig.rootPath, 
                          folder: folderPath]
        invokeWebScript(this.&post, wsURL, parameters)
    }

    def uploadFile(params) {

        def customer = params.customer
        def document = params.document
        def validTo = params.validTo
        def tags = params.tags
        def documentVersion = params.documentVersion
        def folderPath = absolutePathForCustomer(customer)
        def createFoldersResult = createFolders(folderPath)
        if (!createFoldersResult.success) {
            return createFoldersResult
        }
        def wsURL = '/coop/bancocredicoop/proyectos/gd/upload.json'
        def parameters = [folderPath: folderPath,
                          title: params.title,  
                          documentName: params.documentName,
                          description: "Document for ${customer}",
                          validTo: validTo,
                          tags: tags]

        def executeMethod = { client, url, params_ -> 
            postMultipart(client, url, document, params_)   
        }
        invokeWebScript(executeMethod, wsURL, parameters) 
    }

    def hello() {
        def wsURL = '/coop/bancocredicoop/proyectos/gd/hello.json'
        invokeWebScript(this.&get, wsURL)
    }

    def addUserToGroup(userName, groupName) {
        def wsURL = '/coop/bancocredicoop/proyectos/gd/addusertogroup.json'
        def parameters = [userName: userName, 
                          groupName: groupName]
        invokeWebScript(this.&post, wsURL, parameters)
    }

    def removeUserFromGroup(userName, groupName) {
        def wsURL = '/coop/bancocredicoop/proyectos/gd/removeuserfromgroup.json'
        def parameters = [userName: userName, 
                          groupName: groupName]
        invokeWebScript(this.&post, wsURL, parameters)
    }

    private def parseWebScriptResponse(response) {

        def statusCode = response.getStatusLine().getStatusCode()

        def getMessageFromJson = { text -> 
            try {
                def objectResponse = new JsonSlurper().parseText(text)
                return [success: objectResponse.success, 
                        message: objectResponse.message,
                        data: objectResponse.data]
            } catch(Exception e) {  
                return [success: false, 
                        message: "HTTP Status: ${statusCode}, message: n/a",
                        data: e.toString()]
            }
        }

        def body = EntityUtils.toString(response.getEntity())
        def message = getMessageFromJson(body)
        if (statusCode >= 400 || !message.success) {
            def errorMessage = "Error in Alfresco Web Script: ${message.message}"
            return [success: false, message: errorMessage]
        } else {
            return message 
        }

    }

    def absolutePathForCustomer(idCustomer) {
        def normIdCustomer = idCustomer.toString().padLeft(10, '0')
        def lastTenRange = normIdCustomer.size()-10..normIdCustomer.size()-1
        def lastTenChars = normIdCustomer.getAt(lastTenRange)
        def folders = ["${alfrescoConfig.rootPath}"]
        lastTenChars.eachMatch('[0-9]{2}', {
            folders.add(it)
        })
        folders.add(idCustomer)
        folders.join('/')
    }

    def getHttpClient() {
        HttpClients.createDefault()
    }

}


